/* eslint-disable no-return-assign */
import { isBoundFunction, rawDefineProperty, isBoolean } from '../libs/utils';

function isBoundedFunction(value = { __MICRO_APP_IS_BOUND_FUNCTION__: false }) {
  if (isBoolean(value.__MICRO_APP_IS_BOUND_FUNCTION__))
    return value.__MICRO_APP_IS_BOUND_FUNCTION__;
  return (value.__MICRO_APP_IS_BOUND_FUNCTION__ = isBoundFunction(value));
}

function isConstructor(value = { __MICRO_APP_IS_CONSTRUCTOR__: false }) {
  if (isBoolean(value.__MICRO_APP_IS_CONSTRUCTOR__)) return value.__MICRO_APP_IS_CONSTRUCTOR__;

  const valueStr = value.toString();

  const result =
    (value.prototype?.constructor === value &&
      Object.getOwnPropertyNames(value.prototype).length > 1) ||
    /^function\s+[A-Z]/.test(valueStr) ||
    /^class\s+/.test(valueStr);

  return (value.__MICRO_APP_IS_CONSTRUCTOR__ = result);
}

export default function bindFunctionToRawWindow(rawWindow, value) {
  if (value.__MICRO_APP_BOUND_WINDOW_FUNCTION__) return value.__MICRO_APP_BOUND_WINDOW_FUNCTION__;

  if (!isConstructor(value) && !isBoundedFunction(value)) {
    const bindRawWindowValue = value.bind(rawWindow);

    for (const key in value) {
      bindRawWindowValue[key] = value[key];
    }

    if (value.hasOwnProperty('prototype')) {
      rawDefineProperty(bindRawWindowValue, 'prototype', {
        value: value.prototype,
        configurable: true,
        enumerable: false,
        writable: true,
      });
    }

    return (value.__MICRO_APP_BOUND_WINDOW_FUNCTION__ = bindRawWindowValue);
  }

  return value;
}
